package com.ib.algokit;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.activequant.broker.IOrderTracker;
import org.activequant.calendar.IExchangeCalendar;
import org.activequant.calendar.NASDAQCalendarSettings;
import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.account.Order;
import org.activequant.core.domainmodel.account.Portfolio;
import org.activequant.core.domainmodel.data.Candle;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.data.QuoteSeries;
import org.activequant.core.domainmodel.data.TickerNews;
import org.activequant.core.domainmodel.data.TradeIndication;
import org.activequant.core.types.Currency;
import org.activequant.core.types.OrderSide;
import org.activequant.core.types.OrderType;
import org.activequant.core.types.TimeStamp;
import org.activequant.core.util.TimeStampFormat;
import org.activequant.math.algorithms.WEMAAccumulator;
import org.activequant.tradesystem.types.Mode;
import org.activequant.util.charting.IPlotService;
import org.activequant.util.charting.TimeseriesCombinedChart;
import org.apache.log4j.Logger;

import com.ib.algokit.forex.ChangeDetails;
import com.ib.algokit.forex.CurrencyPairs;
import com.ib.algokit.forex.ForexPairStateRanges;
import com.ib.algokit.forex.ForexPairStates;
import com.ib.algokit.forex.Path;
import com.ib.algokit.forex.PathLeg;
import com.ib.algokit.history.BuySignalEvent;
import com.ib.algokit.history.LimitOrderPeriodExtendedEvent;
import com.ib.algokit.history.LimitOrderUpdatedForNoProfitEvent;
import com.ib.algokit.history.MainOrderInactiveTimeoutPeriodEvent;
import com.ib.algokit.history.ProfitLimitClosedEvent;
import com.ib.algokit.history.SellSignalEvent;
import com.ib.algokit.history.SignalDirectionChangeEvent;
import com.ib.algokit.history.StopLossClosedEvent;
import com.ib.algokit.history.TradeEvent;
import com.ib.algokit.ib.CommissionsCalculator;
import com.ib.algokit.ib.IBPortfolioManagingBroker;
import com.ib.algokit.ib.IBPortfolioManagingBroker.IBPosition;

public class ForexAnalyzerUtils implements AnalyzerPluginInterface{
	/**
	 * long NanoSecondsToWaitOnExecution=2*InstrumentAnalyzer.ONE_HOUR;// 2 hours in nanoseconds
	 */
	long NanoSecondsToWaitOnExecution=2*InstrumentAnalyzer.ONE_HOUR;
	/**
	 * public static int EMA_MULTILIER=10;
	 */
	public static int EMA_MULTILIER=10;

//	private double[] longPercChangeLimit=new double[]{-1, 0};
//
//	private double[] shortPercChangeLimit=new double[]{0, 1};


	/**
	 * public static long WINDOW_SIZE_IN_MS=8*3600000;
	 */
	public static long WINDOW_SIZE_IN_MS=8*3600000;//8 hours in millis
	/**
	 * private final InstrumentAnalyzer instrumentAnalyzer;
	 */
	private final InstrumentAnalyzer instrumentAnalyzer;
	/**
	 * private final EMA ema;
	 */
	private final TraderProxy ema;

	private IPlotService<TimeStamp> todayPriceSeriesPlot = null;
	private IPlotService<TimeStamp> todayRecomendationSeriesPlot = null;
	private IPlotService<TimeStamp> todayQuantitySeriesPlot = null;
	private IPlotService.ISeriesService<TimeStamp> todayRecomendationSeriesService = null;
	private IPlotService.ISeriesService<TimeStamp> todayQuoteBidSeriesService = null;
	private IPlotService.ISeriesService<TimeStamp> todayQuoteAskSeriesService = null;	
	private IPlotService.ISeriesService<TimeStamp> todayQuoteBidQuantitySeriesService = null;
	private IPlotService.ISeriesService<TimeStamp> todayQuoteAskQuantitySeriesService = null;

	private boolean plotQuoteBidPriceSeries=true;
	private boolean plotQuoteAskPriceSeries=true;
	private boolean plotRecomendationSeries=false;
	private boolean showChart=true;//!RunMe.isRealRun;//if this is false there will be no chart at all
	/**
	 * private static AtomicBoolean hasInitializedAllAnalyzers=new AtomicBoolean(false)
	 */
	private static AtomicBoolean hasInitializedAllAnalyzers=new AtomicBoolean(false);
	/**
	 * private boolean hasEnoughData=false;
	 */
	private boolean hasEnoughData=false;
	/**
	 * private final WEMAAccumulator fastBidPriceAccumulator = new WEMAAccumulator();<br/>
	 * Its period(double) gets set to 10000000
	 */
	private final WEMAAccumulator fastBidPriceAccumulator = new WEMAAccumulator();
	/**
	 * private final WEMAAccumulator fastAskPriceAccumulator = new WEMAAccumulator();<br/>
	 * Its period(double) gets set to 10000000
	 */
	private final WEMAAccumulator fastAskPriceAccumulator = new WEMAAccumulator();
	/**
	 * private final WEMAAccumulator slowBidPriceAccumulator = new WEMAAccumulator();<br/>
	 * Its period(double) gets set to 100000000.0
	 */
	private final WEMAAccumulator slowBidPriceAccumulator = new WEMAAccumulator();
	/**
	 * private final WEMAAccumulator slowAskPriceAccumulator = new WEMAAccumulator();<br/>
	 * Its period(double) gets set to 100000000.0
	 */
	private final WEMAAccumulator slowAskPriceAccumulator = new WEMAAccumulator();

//	boolean actOnRecommendation=true;
	/**
	 * private final static Map&lt;String,ChangeDetails&gt; trends = new HashMap&lt;String,ChangeDetails&gt;();
	 */
	private final static Map<String,ChangeDetails> trends = new HashMap<String,ChangeDetails>();

	public final static TimeStampFormat fmt = new TimeStampFormat("yyyy/MM/dd");//("yyyy/MM/dd");
	{
		//formatter.setTimeZone(exchangeCalendar.getTimeZone());
		//formatter.setTimeZone(TimeZone.getDefault());
		fmt.setTimeZone(NASDAQCalendarSettings.getInstance().getTimeZone());
	}
	//	private static TimeStamp useKnowledgeAfter=fmt.parse("2011/03/25");

	private static final Map<InstrumentSpecification,List<TradeEvent>> historyPerInstrument=new HashMap<InstrumentSpecification,List<TradeEvent>>();

	private static boolean historyTrackingEnabled=RunMe.mode==Mode.BACKTEST;//false

	private List<ForexPairStates> pairStateHistory=null;//new LinkedList<ForexPairStates>();

	private static final Map<String,List<Path>> alternativePathsPerPair=new HashMap<String,List<Path>>();

	private final String thisForexPair;

	private final String underlying;

	private final String thisSymbol;

	private double longAvgAltPercChangeBegin;

	private double longAvgAltPercChangeEnd;

	private double shortAvgAltPercChangeBegin;

	private double shortAvgAltPercChangeEnd;
	
	private double shortPercChangeBegin;

	private double shortPercChangeEnd;

	private double longPercChangeBegin;

	private double longPercChangeEnd;
        
        
    private double shortDeviationBegin;

	private double shortDeviationEnd;

	private double longDeviationBegin;

	private double longDeviationEnd;
	
	private boolean accumulatorRealtimePeriodSet=false;
        
	public static Map<String,ChangeDetails> getTrends() {
		return trends;
	}

    public static AtomicBoolean getHasInitializedAllAnalyzers(){
    	return hasInitializedAllAnalyzers;
    }

    public static Map<String,List<Path>> getAlternativePathsPerPair(){
    	return alternativePathsPerPair;
    }

	public static boolean isHistoryTrackingEnabled() {
		return historyTrackingEnabled;
	}

	public static Map<InstrumentSpecification, List<TradeEvent>> getHistoryPerInstrument() {
		return historyPerInstrument;
	}

	public static void addHistory(TradeEvent event) {
		InstrumentSpecification instrumentSpecification = event.getInstrumentSpecification();
		List<TradeEvent> instrumentEvents = historyPerInstrument.get(instrumentSpecification);
		if (instrumentEvents == null) {
			instrumentEvents = new ArrayList<TradeEvent>();
		}
		instrumentEvents.add(event);
		historyPerInstrument.put(instrumentSpecification, instrumentEvents);
	}
	/**
	 * Constructs a new AnalyzerUtils using the given instrumentAnalyzer(InstrumentAnalyzer) to set its associated instrumentAnalyzer(InstrumentAnalyzer)
	 * @param instrumentAnalyzer
	 */
	public ForexAnalyzerUtils(InstrumentAnalyzer instrumentAnalyzer){
		this.instrumentAnalyzer=instrumentAnalyzer;
		this.ema=instrumentAnalyzer.getEMA();
		Currency symbolCurrency=Currency.toClass(instrumentAnalyzer.getInstrumentSpecification().getSymbol().toString());
//		double slowPeriod=CurrencyPairs.convertFromUSDFixedRate(100000000, symbolCurrency);
//		this.slowBidPriceAccumulator.setPeriod(slowPeriod);
//		this.slowAskPriceAccumulator.setPeriod(slowPeriod);
//		double fastPeriod=CurrencyPairs.convertFromUSDFixedRate(10000000, symbolCurrency);//10000000
//		this.fastBidPriceAccumulator.setPeriod(fastPeriod);
//		this.fastAskPriceAccumulator.setPeriod(fastPeriod);

		Currency underlyingCurrency=instrumentAnalyzer.getInstrumentSpecification().getCurrency();
		thisSymbol = instrumentAnalyzer.getInstrumentSpecification().getSymbol().toString();
		underlying = underlyingCurrency.toString();
		thisForexPair=symbolCurrency+"/"+underlying;
		shortPercChangeBegin=-1;
		shortPercChangeEnd=1;

		longPercChangeBegin=-1;
		longPercChangeEnd=1;

		//set pair specific variables

		longAvgAltPercChangeBegin=-1;
		longAvgAltPercChangeEnd=1d;
		shortAvgAltPercChangeBegin= -1d;//0.00015d;
		shortAvgAltPercChangeEnd=1;
                
                
        longDeviationBegin=0;//0
		longDeviationEnd=0.0003;
		shortDeviationBegin=0;
		shortDeviationEnd=0.0003;//
	}
	/**
	 * private TimeseriesCombinedChart timeseriesCombinedChart = null;<br/>
	 * this holds the whole chart
	 */
	private TimeseriesCombinedChart timeseriesCombinedChart = null;
	/**
	 * returns the associated timeseriesCombinedChart(TimeseriesCombinedChart)
	 * @return
	 */
	public TimeseriesCombinedChart getTimeseriesCombinedChart(){
		return timeseriesCombinedChart;
	}

	public void onCandle(Candle liveCandle){

	}

	public void onTradeIndication(TradeIndication liveTradeIndication){

	}	

	public void onQuote(Quote liveQuote){
		if(showChart){
			if(timeseriesCombinedChart==null){
				String symbol = instrumentAnalyzer.getInstrumentSpecification().getSymbol().toString();
				String currency = instrumentAnalyzer.getInstrumentSpecification().getCurrency().toString();
				String chartTitle=symbol+"/"+currency+"@"+fmt.format(liveQuote.getTimeStamp());
				initTimeseriesCombinedChart(chartTitle);
			}
			//todayQuoteBidSeriesService.addDataPoint(liveQuote.getTimeStamp(), liveQuote.getBidPrice());
			//todayQuoteAskSeriesService.addDataPoint(liveQuote.getTimeStamp(), liveQuote.getAskPrice());
			todayQuoteBidQuantitySeriesService.addDataPoint(liveQuote.getTimeStamp(), liveQuote.getBidQuantity());
			todayQuoteAskQuantitySeriesService.addDataPoint(liveQuote.getTimeStamp(), -liveQuote.getAskQuantity());
		}
		
		if(!accumulatorRealtimePeriodSet){
			
			Currency symbolCurrency=Currency.toClass(instrumentAnalyzer.getInstrumentSpecification().getSymbol().toString());
			double slowPeriod=CurrencyPairs.convertFromUSDLastKnownRealTimeRate(100000000, symbolCurrency);
			double fastPeriod=CurrencyPairs.convertFromUSDLastKnownRealTimeRate(10000000, symbolCurrency);//10000000
			if(fastPeriod!=0&&slowPeriod!=0){
				this.slowBidPriceAccumulator.setPeriod(slowPeriod);
				this.slowAskPriceAccumulator.setPeriod(slowPeriod);
				
				this.fastBidPriceAccumulator.setPeriod(fastPeriod);
				this.fastAskPriceAccumulator.setPeriod(fastPeriod);
				accumulatorRealtimePeriodSet=true;
			}			
		}
		
		if(accumulatorRealtimePeriodSet){		
			slowBidPriceAccumulator.accumulate(liveQuote.getBidPrice(), liveQuote.getBidQuantity());
			fastBidPriceAccumulator.accumulate(liveQuote.getBidPrice(), liveQuote.getBidQuantity());
			//todayQuoteBidSeriesService.addDataPoint(liveQuote.getTimeStamp(), slowBidPriceAccumulator.getValue());
			slowAskPriceAccumulator.accumulate(liveQuote.getAskPrice(), liveQuote.getAskQuantity());
			fastAskPriceAccumulator.accumulate(liveQuote.getAskPrice(), liveQuote.getAskQuantity());
			//todayQuoteAskSeriesService.addDataPoint(liveQuote.getTimeStamp(), slowAskPriceAccumulator.getValue());	
	
			if(!hasEnoughData && slowBidPriceAccumulator.getTotalLength()<(EMA_MULTILIER*slowBidPriceAccumulator.getPeriod()) || 
				slowAskPriceAccumulator.getTotalLength()<(EMA_MULTILIER*slowAskPriceAccumulator.getPeriod())){
				return;
			}else{
				hasEnoughData=true;
			}
			//String symbol = instrumentAnalyzer.getInstrumentSpecification().getSymbol().toString();
			getRecommendationSignal(liveQuote);
		}
	}

	public void onTickerNews(TickerNews liveTickerNews){

	}
//	public static PForexPairStateMemory memory = RunMe.useMemory?(RunMe.isRealRun? PForexPairStateMemory.readFromFile():PForexPairStateMemory.readFromFile(PForexPairStateMemory.getMemoryFileForCurrentDate())):null;
	/**
	 * this implements strategy #1 on opensourcewallstreet.com (watch pairs for certain underlying and when 5 th pair's short term EMA crosses long term EMA, go long on that pair)
	 */
	public void getRecommendationSignal(Quote liveQuote){
		boolean sendHeartBeat=true;
		double thisSlowBidValue = slowBidPriceAccumulator.getValue();
		double thisFastBidValue = fastBidPriceAccumulator.getValue();
		ChangeDetails changeDetails=null;
		double percChange=(thisFastBidValue-thisSlowBidValue)/thisFastBidValue;
		if(Double.isNaN(percChange)){
			throw new RuntimeException("something is wrong");
		}
		changeDetails=new ChangeDetails(thisForexPair, thisSlowBidValue, thisFastBidValue, percChange);
		trends.put(thisForexPair, changeDetails);
		boolean openPositionsForShow=true;//this is for demo purposes, generate random number between 0 - 1 to decide what to do
		if(openPositionsForShow){
			double rand=Math.random();
			if(rand<0.01){
				actUponRecommendation(Recommendation.BUY, pairStateHistory);
			}else if(rand<0.99){
				actUponRecommendation(Recommendation.SELL, pairStateHistory);
			}else{
				actUponRecommendation(Recommendation.DO_NOTHING, pairStateHistory);
			}
			
			return;
		}
		
		
		
		
		if(!hasInitializedAllAnalyzers.get()){
			if(trends.size()<InstrumentAnalyzer.getInstrumentAnalyzers().size()){
				return;
			}else{
				hasInitializedAllAnalyzers.set(true);
			}
		}
		TimeStamp timeStamp=liveQuote.getTimeStamp();
		List<Path> alternativePaths= alternativePathsPerPair.get(thisForexPair);
		double alternativePathsChange=0;
		int totalNumberOfPaths=0;
		Map<String,Double> pathWeightsUsed=new HashMap<String,Double>();
		if(alternativePaths!=null){
			totalNumberOfPaths=alternativePaths.size();
			for(Path alternativePath:alternativePaths){
				List<PathLeg> pathLegs=alternativePath.getPathLegs();
				double pathWeight=0;
				StringBuffer pathPairs=new StringBuffer();
				for(PathLeg pathLeg:pathLegs){
					String legForexPair=pathLeg.getForexPair();
					ChangeDetails legChangeDetails=trends.get(legForexPair);
					if(pathLeg.getSameDirection()){
						pathWeight+=legChangeDetails.getPercChange();
					}else{
						pathWeight-=legChangeDetails.getPercChange();
					}
					pathPairs.append(legForexPair);
				}
				alternativePathsChange+=pathWeight;
				pathWeightsUsed.put(pathPairs.toString(), pathWeight);
			}
		}else{
			List<InstrumentAnalyzer> analyzersMatchingSymbolBySymbol = CurrencyPairs.getForexAnalyzersBySymbol(thisSymbol);
			List<InstrumentAnalyzer> analyzersMatchingUnderlyingByUnderlying = CurrencyPairs.getForexAnalyzersByUnderlying(underlying);
			List<InstrumentAnalyzer> analyzersMatchingSymbolByUnderlying = CurrencyPairs.getForexAnalyzersBySymbol(underlying);
			List<InstrumentAnalyzer> analyzersMatchingUnderlyingBySymbol = CurrencyPairs.getForexAnalyzersByUnderlying(thisSymbol);
			Set<String> firstLegPairsUsed=new HashSet<String>();
			Set<String> secondLegPairsUsed=new HashSet<String>();
			alternativePaths=new ArrayList<Path>();
			//Ex. for "EUR/USD" gets the other ones with "EUR/*"
			if(analyzersMatchingSymbolBySymbol!=null){
				for(InstrumentAnalyzer firstLegAnalyzer:analyzersMatchingSymbolBySymbol){
					String otherUnderlying=firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
					if(!otherUnderlying.equals(underlying)){
						String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
						String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
						String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
						ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
						InstrumentAnalyzer secondLegAnalyzer = CurrencyPairs.getForexAnalyzer(otherUnderlying+"/"+underlying);						
						if(secondLegAnalyzer!=null){
							//got a 2 leg path\                                                       
							String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
							String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
							String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
							ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
							firstLegPairsUsed.add(firstLegForexPair);
							secondLegPairsUsed.add(secondLegForexPair);
							double pathWeight=firstLegChangeDetails.getPercChange()+secondLegChangeDetails.getPercChange();
							alternativePathsChange+=pathWeight;
							pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
							totalNumberOfPaths++;

							//caching path for next time
							List<PathLeg> legs=new ArrayList<PathLeg>();
							PathLeg firstLeg=new PathLeg(firstLegForexPair,true);
							legs.add(firstLeg);
							PathLeg secondLeg=new PathLeg(secondLegForexPair,true);
							legs.add(secondLeg);
							Path path=new Path(legs);
							alternativePaths.add(path);
						}else{
							secondLegAnalyzer = CurrencyPairs.getForexAnalyzer(underlying+"/"+otherUnderlying);
							if(secondLegAnalyzer!=null){
								//got a 2 leg path\
								String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
								String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
								String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
								ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
								firstLegPairsUsed.add(firstLegForexPair);
								secondLegPairsUsed.add(secondLegForexPair);
								double pathWeight=firstLegChangeDetails.getPercChange()-secondLegChangeDetails.getPercChange();
								alternativePathsChange+=pathWeight;
								pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
								totalNumberOfPaths++;

								//caching path for next time
								List<PathLeg> legs=new ArrayList<PathLeg>();
								PathLeg firstLeg=new PathLeg(firstLegForexPair,true);
								legs.add(firstLeg);
								PathLeg secondLeg=new PathLeg(secondLegForexPair,false);
								legs.add(secondLeg);
								Path path=new Path(legs);
								alternativePaths.add(path);
							}
						}
					}
				}
			}

			//Ex. for "EUR/USD" gets the other ones with "*/USD"
			if(analyzersMatchingUnderlyingByUnderlying!=null){
				for(InstrumentAnalyzer secondLegAnalyzer:analyzersMatchingUnderlyingByUnderlying){
					String otherSymbol=secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
					if(!otherSymbol.equals(thisSymbol)){
						String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
						String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
						String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
						if(!secondLegPairsUsed.add(secondLegForexPair)){
							continue;
						}
						ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
						InstrumentAnalyzer firstLegAnalyzer = CurrencyPairs.getForexAnalyzer(thisSymbol+"/"+otherSymbol);	
						if(firstLegAnalyzer!=null){
							//got a 2 leg path\
							String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
							String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
							String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
							ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
							firstLegPairsUsed.add(firstLegForexPair);
							double pathWeight=firstLegChangeDetails.getPercChange()+secondLegChangeDetails.getPercChange();
							alternativePathsChange+=pathWeight;
							pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
							totalNumberOfPaths++;

							//caching path for next time
							List<PathLeg> legs=new ArrayList<PathLeg>();
							PathLeg firstLeg=new PathLeg(firstLegForexPair,true);
							legs.add(firstLeg);
							PathLeg secondLeg=new PathLeg(secondLegForexPair,true);
							legs.add(secondLeg);
							Path path=new Path(legs);
							alternativePaths.add(path);
						}else{
							firstLegAnalyzer = CurrencyPairs.getForexAnalyzer(otherSymbol+"/"+thisSymbol);
							if(firstLegAnalyzer!=null){
								//got a 2 leg path\
								String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
								String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
								String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
								ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
								firstLegPairsUsed.add(firstLegForexPair);
								double pathWeight=-firstLegChangeDetails.getPercChange()+secondLegChangeDetails.getPercChange();
								alternativePathsChange+=pathWeight;
								pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
								totalNumberOfPaths++;

								//caching path for next time
								List<PathLeg> legs=new ArrayList<PathLeg>();
								PathLeg firstLeg=new PathLeg(firstLegForexPair,false);
								legs.add(firstLeg);
								PathLeg secondLeg=new PathLeg(secondLegForexPair,true);
								legs.add(secondLeg);
								Path path=new Path(legs);
								alternativePaths.add(path);
							}
						}
					}
				}
			}

			//Ex. for "EUR/USD" gets the other ones with "USD/*"
			if(analyzersMatchingSymbolByUnderlying!=null){
				for(InstrumentAnalyzer secondLegAnalyzer:analyzersMatchingSymbolByUnderlying){
					String otherUnderlying=secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
					if(!otherUnderlying.equals(thisSymbol)){
						String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
						String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
						String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
						if(!secondLegPairsUsed.add(secondLegForexPair)){
							continue;
						}
						ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
						InstrumentAnalyzer firstLegAnalyzer = CurrencyPairs.getForexAnalyzer(thisSymbol+"/"+otherUnderlying);
						if(firstLegAnalyzer!=null){
							//got a 2 leg path\
							String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
							String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
							String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
							ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
							firstLegPairsUsed.add(firstLegForexPair);
							double pathWeight=firstLegChangeDetails.getPercChange()-secondLegChangeDetails.getPercChange();
							alternativePathsChange+=pathWeight;
							pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
							totalNumberOfPaths++;

							//caching path for next time
							List<PathLeg> legs=new ArrayList<PathLeg>();
							PathLeg firstLeg=new PathLeg(firstLegForexPair,true);
							legs.add(firstLeg);
							PathLeg secondLeg=new PathLeg(secondLegForexPair,false);
							legs.add(secondLeg);
							Path path=new Path(legs);
							alternativePaths.add(path);
						}else{
							firstLegAnalyzer = CurrencyPairs.getForexAnalyzer(otherUnderlying+"/"+thisSymbol);
							if(firstLegAnalyzer!=null){
								//got a 2 leg path\
								String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
								String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
								String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
								ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
								firstLegPairsUsed.add(firstLegForexPair);
								double pathWeight=-firstLegChangeDetails.getPercChange()-secondLegChangeDetails.getPercChange();
								alternativePathsChange+=pathWeight;
								pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
								totalNumberOfPaths++;

								//caching path for next time
								List<PathLeg> legs=new ArrayList<PathLeg>();
								PathLeg firstLeg=new PathLeg(firstLegForexPair,false);
								legs.add(firstLeg);
								PathLeg secondLeg=new PathLeg(secondLegForexPair,false);
								legs.add(secondLeg);
								Path path=new Path(legs);
								alternativePaths.add(path);
							}
						}
					}
				}
			}

			//Ex. for "EUR/USD" gets the other ones with "*/EUR"
			if(analyzersMatchingUnderlyingBySymbol!=null){
				for(InstrumentAnalyzer firstLegAnalyzer:analyzersMatchingUnderlyingBySymbol){
					String otherSymbol=firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
					if(!otherSymbol.equals(underlying)){
						String firstLegSymbol = firstLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
						String firstLegCurrency = firstLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
						String firstLegForexPair=firstLegSymbol+"/"+firstLegCurrency;
						if(!firstLegPairsUsed.add(firstLegForexPair)){
							continue;
						}
						ChangeDetails firstLegChangeDetails=trends.get(firstLegForexPair);
						InstrumentAnalyzer secondLegAnalyzer = CurrencyPairs.getForexAnalyzer(otherSymbol+"/"+underlying);
						if(secondLegAnalyzer!=null){
							//got a 2 leg path\
							String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
							String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
							String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
							ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
							secondLegPairsUsed.add(secondLegForexPair);
							double pathWeight=-firstLegChangeDetails.getPercChange()+secondLegChangeDetails.getPercChange();
							alternativePathsChange+=pathWeight;
							pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
							totalNumberOfPaths++;

							//caching path for next time
							List<PathLeg> legs=new ArrayList<PathLeg>();
							PathLeg firstLeg=new PathLeg(firstLegForexPair,false);
							legs.add(firstLeg);
							PathLeg secondLeg=new PathLeg(secondLegForexPair,true);
							legs.add(secondLeg);
							Path path=new Path(legs);
							alternativePaths.add(path);
						}else{
							secondLegAnalyzer = CurrencyPairs.getForexAnalyzer(underlying+"/"+otherSymbol);
							if(secondLegAnalyzer!=null){
								//got a 2 leg path\
								String secondLegSymbol = secondLegAnalyzer.getInstrumentSpecification().getSymbol().toString();
								String secondLegCurrency = secondLegAnalyzer.getInstrumentSpecification().getCurrency().toString();
								String secondLegForexPair=secondLegSymbol+"/"+secondLegCurrency;
								ChangeDetails secondLegChangeDetails=trends.get(secondLegForexPair);
								secondLegPairsUsed.add(secondLegForexPair);
								double pathWeight=-firstLegChangeDetails.getPercChange()-secondLegChangeDetails.getPercChange();
								alternativePathsChange+=pathWeight;
								pathWeightsUsed.put(firstLegForexPair+secondLegForexPair, pathWeight);
								totalNumberOfPaths++;

								//caching path for next time
								List<PathLeg> legs=new ArrayList<PathLeg>();
								PathLeg firstLeg=new PathLeg(firstLegForexPair,false);
								legs.add(firstLeg);
								PathLeg secondLeg=new PathLeg(secondLegForexPair,false);
								legs.add(secondLeg);
								Path path=new Path(legs);
								alternativePaths.add(path);
							}
						}
					}
				}
			}
			alternativePathsPerPair.put(thisForexPair,alternativePaths);
		}
		double averageAlternativePercChange=alternativePathsChange/totalNumberOfPaths;
		Recommendation recommendation=null;
		
			if((percChange>=shortPercChangeBegin&&percChange<=shortPercChangeEnd)&& /*((averageAlternativePercChange-percChange)>0.0002)&&*/ (averageAlternativePercChange>=shortAvgAltPercChangeBegin && averageAlternativePercChange<=shortAvgAltPercChangeEnd)){
				double t1 = 0.0;
				for(Entry<String,Double> entry:pathWeightsUsed.entrySet()){
					double diff=entry.getValue()-averageAlternativePercChange;
					t1+=diff*diff;
				}
				double deviation = Math.sqrt(t1 / (double) pathWeightsUsed.size());
//				if(deviation>=shortDeviationBegin && deviation<=shortDeviationEnd){//<0.0003
//					recommendation=Recommendation.SELL;
                    recommendation=Recommendation.BUY;
					//if(!"EUR/JPY".equals(thisForexPair)){
					//	actUponRecommendation(Recommendation.DO_NOTHING, pairStateHistory);
					//	return;
					//	recommendation=Recommendation.BUY;
					//}
					Set<ChangeDetails> currentPairChangeDetails=new HashSet<ChangeDetails>();
					getChangeDetailsLists(currentPairChangeDetails);
					ForexPairStates forexPairStates=new ForexPairStates(currentPairChangeDetails, deviation, percChange, averageAlternativePercChange);
					pairStateHistory=new ArrayList<ForexPairStates>();
					pairStateHistory.add(forexPairStates);
					if(RunMe.mode==Mode.LIVE){
						StringBuffer msg=new StringBuffer();	
						msg.append((recommendation==Recommendation.SELL)?" sell ":" buy ");
						msg.append(thisForexPair);
						//msg.append(" percChange="+percChange);
						msg.append(" @"+InstrumentAnalyzer.formatTimeStamp(timeStamp));			
						instrumentAnalyzer.getLog().info(msg.toString());
					}
					actUponRecommendation(recommendation, pairStateHistory);
					sendHeartBeat=false;
//				}
			}else if((percChange>=longPercChangeBegin&&percChange<=longPercChangeEnd) && /*((percChange-averageAlternativePercChange)>0.0002) &&*/ (averageAlternativePercChange>=longAvgAltPercChangeBegin && averageAlternativePercChange<=longAvgAltPercChangeEnd)){
				double t1 = 0.0;
				for(Entry<String,Double> entry:pathWeightsUsed.entrySet()){
					double diff=entry.getValue()-averageAlternativePercChange;
					t1+=diff*diff;
				}
				double deviation = Math.sqrt(t1 / (double) pathWeightsUsed.size());
					recommendation=Recommendation.BUY;
					Set<ChangeDetails> currentPairChangeDetails=new HashSet<ChangeDetails>();
					getChangeDetailsLists(currentPairChangeDetails);
					ForexPairStates forexPairStates=new ForexPairStates(currentPairChangeDetails, deviation, percChange, averageAlternativePercChange);
					pairStateHistory=new ArrayList<ForexPairStates>();
					pairStateHistory.add(forexPairStates);
					if(RunMe.mode==Mode.LIVE){
						StringBuffer msg=new StringBuffer();
						msg.append((recommendation==Recommendation.SELL)?" sell ":" buy ");
						msg.append(thisForexPair);
						//msg.append(" percChange="+percChange);
						msg.append(" @"+InstrumentAnalyzer.formatTimeStamp(timeStamp));
						instrumentAnalyzer.getLog().info(msg.toString());
					}
					actUponRecommendation(recommendation, pairStateHistory);
					sendHeartBeat=false;
//				}
			}
		
		if(sendHeartBeat){
			actUponRecommendation(Recommendation.DO_NOTHING, pairStateHistory);
		}
	}
	
	public void initTimeseriesCombinedChart(String frameTitle){
		if(timeseriesCombinedChart==null){
			timeseriesCombinedChart = new TimeseriesCombinedChart(frameTitle, null/*"time"*/);
			timeseriesCombinedChart.setWindowSizeMillis(WINDOW_SIZE_IN_MS);
		}
		if(todayPriceSeriesPlot==null){
			todayPriceSeriesPlot=timeseriesCombinedChart.createPlot("price", 3, false, true);
		}
		if(plotRecomendationSeries&&todayRecomendationSeriesPlot==null){
			todayRecomendationSeriesPlot=timeseriesCombinedChart.createPlot("recommendation", 2, false, false);
			todayRecomendationSeriesService = todayRecomendationSeriesPlot.newSeries("recomendation signal");
		}

		if(todayQuantitySeriesPlot==null){
			todayQuantitySeriesPlot=timeseriesCombinedChart.createPlot("bid/ask quantity", 2, false, false);
		}

		if(plotQuoteBidPriceSeries&&todayQuoteBidSeriesService==null){
			todayQuoteBidSeriesService = todayPriceSeriesPlot.newSeries("bid");
		}
		if(plotQuoteAskPriceSeries&&todayQuoteAskSeriesService==null){
			todayQuoteAskSeriesService = todayPriceSeriesPlot.newSeries("ask");
		}

		if(todayQuoteBidQuantitySeriesService==null){
			todayQuoteBidQuantitySeriesService = todayQuantitySeriesPlot.newSeries("bid size");
		}
		if(todayQuoteAskQuantitySeriesService==null){
			todayQuoteAskQuantitySeriesService = todayQuantitySeriesPlot.newSeries("ask size");
		}
		//GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
		//GraphicsDevice[] gs = ge.getScreenDevices(); // Get size of each screen 
		//for (int i = 0; i < gs.length; i++) {
		//	DisplayMode dm = gs[i].getDisplayMode();
		//	int screenWidth = dm.getWidth();
		//	int screenHeight = dm.getHeight();
		//	timeseriesCombinedChart.setBounds(0, 0, screenWidth, screenHeight);
		//}
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		timeseriesCombinedChart.setBounds(0, 0, (int)dim.getWidth(), (int)dim.getHeight());
		timeseriesCombinedChart.setVisible(true);
	}
	/**
	 * List&lt;DailyData&gt; dailyData=null;
	 */
	//	List<DailyData> dailyData=null;

	public double getTargetProfit(){
		return 10;
	}

	public long getNanoSecondsToWaitOnExecution(){
		return NanoSecondsToWaitOnExecution;
	}
	public void updateExtremes(Candle liveCandle) {
		// TODO Auto-generated method stub
	}
	public void initializeTMINUS5DWeightedDayFeatures() {
		// TODO Auto-generated method stub
	}

	/**
	 * Main Buy Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainBuyOrderAcceptedAndWorking(ForexBracketPositionService.Tracker positionTracker){
		boolean isMainOrderWorking = isMainOrderAcceptedAndWorking(positionTracker);
		if(!isMainOrderWorking){
			return false;
		}		
		ForexBracketPositionService.Tracker posTracker=(ForexBracketPositionService.Tracker)positionTracker;
		Order order=null;
		if(posTracker!=null){
			order=posTracker.getMainOrderTracker().getOrder();			
		}
		return order!=null && order.getOrderSide()==OrderSide.BUY;
	}
	/**
	 * Main Sell Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainSellOrderAcceptedAndWorking(ForexBracketPositionService.Tracker positionTracker){
		boolean isMainOrderWorking = isMainOrderAcceptedAndWorking(positionTracker);
		if(!isMainOrderWorking){
			return false;
		}		
		ForexBracketPositionService.Tracker posTracker=(ForexBracketPositionService.Tracker)positionTracker;
		Order order=null;
		if(posTracker!=null){
			order=posTracker.getMainOrderTracker().getOrder();			
		}
		return order!=null && (order.getOrderSide()==OrderSide.SELL || order.getOrderSide()==OrderSide.SHORT_SELL);
	}
	/**
	 * Main Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainOrderAcceptedAndWorking(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp mainOrderAcceptedTimeStamp=null;
		TimeStamp mainOrderCanceledTimeStamp=null;
		TimeStamp mainOrderCompletedAndCommissionsReceivedTimeStamp=null;
		if(positionTracker!=null){			
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
			mainOrderCanceledTimeStamp = positionTracker.getMainOrderCanceledTimeStamp();
			mainOrderCompletedAndCommissionsReceivedTimeStamp = positionTracker.getMainOrderCompletedAndCommissionsReceivedTimeStamp();

		}
		return positionTracker!=null && mainOrderAcceptedTimeStamp!=null && mainOrderCanceledTimeStamp==null && mainOrderCompletedAndCommissionsReceivedTimeStamp==null;
	}

	public boolean isMainOrderSubmittedAndNotAccepted(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp mainOrderSubmittedTimeStamp=null;
		TimeStamp mainOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			mainOrderSubmittedTimeStamp = positionTracker.getMainOrderSubmittedTimeStamp();
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && mainOrderSubmittedTimeStamp!=null && mainOrderAcceptedTimeStamp==null;
	}

	public boolean isLimitOrderSubmittedAndNotAccepted(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp limitOrderSubmittedTimeStamp=null;
		TimeStamp limitOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			limitOrderSubmittedTimeStamp = positionTracker.getLimitOrderSubmittedTimeStamp();
			limitOrderAcceptedTimeStamp = positionTracker.getLimitOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && limitOrderSubmittedTimeStamp!=null && limitOrderAcceptedTimeStamp==null;
	}

	public boolean isStopOrderSubmittedAndNotAccepted(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp stopOrderSubmittedTimeStamp=null;
		TimeStamp stopOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			stopOrderSubmittedTimeStamp = positionTracker.getStopOrderSubmittedTimeStamp();
			stopOrderAcceptedTimeStamp = positionTracker.getStopOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && stopOrderSubmittedTimeStamp!=null && stopOrderAcceptedTimeStamp==null;
	}

	public boolean hasMainOrderReachedSubmitToAcceptTimeoutPeriod(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp mainOrderSubmittedTimeStamp=null;
		TimeStamp mainOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			mainOrderSubmittedTimeStamp = positionTracker.getMainOrderSubmittedTimeStamp();
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && mainOrderSubmittedTimeStamp!=null && mainOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(mainOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}

	public boolean hasLimitOrderReachedSubmitToAcceptTimeoutPeriod(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp limitOrderSubmittedTimeStamp=null;
		TimeStamp limitOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			limitOrderSubmittedTimeStamp = positionTracker.getLimitOrderSubmittedTimeStamp();
			limitOrderAcceptedTimeStamp = positionTracker.getLimitOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && limitOrderSubmittedTimeStamp!=null && limitOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(limitOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}

	public boolean hasStopOrderReachedSubmitToAcceptTimeoutPeriod(ForexBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp stopOrderSubmittedTimeStamp=null;
		TimeStamp stopOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			stopOrderSubmittedTimeStamp = positionTracker.getStopOrderSubmittedTimeStamp();
			stopOrderAcceptedTimeStamp = positionTracker.getStopOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && stopOrderSubmittedTimeStamp!=null && stopOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(stopOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}
	//	boolean waitForQuotes=true;
	/**
	 * If I haven't already decided to buy and conditions are right, buy
	 */
	public void actUponRecommendation(Recommendation recommendation, List<ForexPairStates> thePairStateHistory){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		InstrumentSpecification instrumentSpecification=instrumentAnalyzer.getInstrumentSpecification();
		if(isHistoryTrackingEnabled()){
			if(recommendation==Recommendation.BUY){
				ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
				addHistory(new BuySignalEvent(currentTimeStamp, "", instrumentSpecification, latestForexPairStates));
			}else if(recommendation==Recommendation.SELL){
				ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
				addHistory(new SellSignalEvent(currentTimeStamp, "", instrumentSpecification, latestForexPairStates));
			}
		}
		//IPositionTracker positionTracker=instrumentAnalyzer.getPositionTracker();
		ForexBracketPositionService.Tracker forexPositionTracker=(ForexBracketPositionService.Tracker)instrumentAnalyzer.getPositionTracker();
		Logger log=instrumentAnalyzer.getLog();
		AtomicReference<QuoteSeries> liveQuoteSeriesBag = instrumentAnalyzer.getLiveQuoteSeriesBag();
		//if(waitForQuotes){
		//	if(liveQuoteSeriesBag==null||liveQuoteSeriesBag.get()==null||liveQuoteSeriesBag.get().size()==0){
		//		log.info("waiting for live Quote");
		//		return;
		//	}else{
		//		waitForQuotes=false;
		//	}
		//}
		Quote latestQuote=liveQuoteSeriesBag.get().get(0);
		double bidPrice=latestQuote.getBidPrice();
		double askPrice=latestQuote.getAskPrice();
		Currency symbolCurrency = Currency.toClass(instrumentSpecification.getSymbol().toString());
		String title=instrumentSpecification.getSymbol().toString()+"/"+instrumentSpecification.getCurrency();
		if(hasMainOrderReachedSubmitToAcceptTimeoutPeriod(forexPositionTracker)){
			StringBuffer msg=new StringBuffer(title+" main order reached timeout between submit and accept events. Something is probably wrong with the order. @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
			IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
			if(mainOrderTracker!=null){
				Order mainOrder = mainOrderTracker.getOrder();
				msg.append(mainOrder.toString());
			}
			log.warn(msg.toString());
		}else if(isMainBuyOrderAcceptedAndWorking(forexPositionTracker)){
			if(recommendation==Recommendation.BUY){
					log.info(title+" I already requested or submitted a BUY order so don't do anything (maybe modify in the future) @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
				
				return;//I already requested or submitted a BUY order so don't do anything (maybe modify in the future)
			}else if(recommendation==Recommendation.SELL){
				if(forexPositionTracker!=null){
					IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
					if(mainOrderTracker!=null){
						log.info(title+" got sell signal, cancelling main buy order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
						mainOrderTracker.cancel();						
						//if main order got partial fill, add that to sell order qty
						double mainOrderFilledQuantity = forexPositionTracker.getMainOrderFilledQuantity();						
						//todo submit new sell order here						
						Order order = new Order();
						order.setInstrumentSpecification(instrumentSpecification);
						order.setOrderTimeStamp(currentTimeStamp);
						order.setOrderSide(OrderSide.SELL);
						order.setLimitPrice(bidPrice);
						order.setOrderType(OrderType.LIMIT);
						double optimalQuantityUSD = CommissionsCalculator.determineOrderQuantityForOptimalMinimumCommission(order);
						double optimalQuantity = symbolCurrency==Currency.USD?optimalQuantityUSD:CurrencyPairs.convert(Currency.USD, optimalQuantityUSD, symbolCurrency);
						if(mainOrderFilledQuantity>0){
							optimalQuantity+=mainOrderFilledQuantity;	
						}
						double roundedOptimalQuantity=CurrencyPairs.roundUpAmount(symbolCurrency, optimalQuantity);
						order.setQuantity(roundedOptimalQuantity);
						ForexBracketPositionService positionService = (ForexBracketPositionService)instrumentAnalyzer.getPositionService();
						instrumentAnalyzer.setPositionTracker(mainOrderFilledQuantity>0?positionService.openPosition(order, mainOrderFilledQuantity, thePairStateHistory):positionService.openPosition(order, 0, thePairStateHistory));
						if(isHistoryTrackingEnabled()){
							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
							addHistory(new SignalDirectionChangeEvent(currentTimeStamp, "\\", instrumentSpecification, latestForexPairStates));							
						}
						return;						
					}
				}
			}
		}else if(isMainSellOrderAcceptedAndWorking(forexPositionTracker)){
			if(recommendation==Recommendation.SELL){
					log.info(title+" I already requested or submitted a SELL order so don't do anything (maybe modify in the future) @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
				return;
			}else if(recommendation==Recommendation.BUY){
				if(forexPositionTracker!=null){
					IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
					if(mainOrderTracker!=null){
						log.info(title+" got buy signal, cancelling main sell order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
						mainOrderTracker.cancel();						
						//if main order got partial fill, add that to buy order qty
						double mainOrderFilledQuantity = forexPositionTracker.getMainOrderFilledQuantity();						
						//todo submit new sell order here						
						Order order = new Order();
						order.setInstrumentSpecification(instrumentSpecification);
						order.setOrderTimeStamp(currentTimeStamp);
						order.setOrderSide(OrderSide.BUY);
						order.setLimitPrice(askPrice);
						order.setOrderType(OrderType.LIMIT);
						double optimalQuantityUSD = CommissionsCalculator.determineOrderQuantityForOptimalMinimumCommission(order);
						double optimalQuantity = symbolCurrency==Currency.USD?optimalQuantityUSD:CurrencyPairs.convert(Currency.USD, optimalQuantityUSD, symbolCurrency);
						if(mainOrderFilledQuantity>0){
							optimalQuantity+=mainOrderFilledQuantity;	
						}
						double roundedOptimalQuantity=CurrencyPairs.roundUpAmount(symbolCurrency, optimalQuantity);
						order.setQuantity(roundedOptimalQuantity);
						ForexBracketPositionService positionService = (ForexBracketPositionService)instrumentAnalyzer.getPositionService();
						instrumentAnalyzer.setPositionTracker(mainOrderFilledQuantity>0?positionService.openPosition(order, mainOrderFilledQuantity, thePairStateHistory):positionService.openPosition(order, 0, thePairStateHistory));
						if(isHistoryTrackingEnabled()){
							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
							addHistory(new SignalDirectionChangeEvent(currentTimeStamp, "/", instrumentSpecification, latestForexPairStates));							
						}
						return;						
					}
				}
			}
		}
		IExchangeCalendar exchangeCalendar = instrumentAnalyzer.getExchangeCalendar();
		Calendar calendar=GregorianCalendar.getInstance(exchangeCalendar.getTimeZone());
		calendar.setTimeInMillis(currentTimeStamp.getDate().getTime());
		int hourOfDay=calendar.get(Calendar.HOUR_OF_DAY);
		int minute=calendar.get(Calendar.MINUTE);
		boolean stopOpeningPositions=hourOfDay>=23; //optional
		boolean closeOpenPositions=hourOfDay>=23||(hourOfDay>=23&&minute>=50);//optional
		if(forexPositionTracker!=null && !forexPositionTracker.isClosed()){
			if(forexPositionTracker.hasMainOrderReachedMaximumInactivePeriod() && (isMainOrderAcceptedAndWorking(forexPositionTracker))){
				IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
				if(mainOrderTracker!=null){
					log.info(title+" cancelling main order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					mainOrderTracker.cancel();
					if(isHistoryTrackingEnabled()){
						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
						addHistory(new MainOrderInactiveTimeoutPeriodEvent(currentTimeStamp, "/", instrumentSpecification, latestForexPairStates));							
					}
				}
				//price moved away from the order, cancel? position could be partially opened as well
				if(forexPositionTracker.getMainOrderFilledQuantity()!=0){
					//cancel main order
					//to do
					//partially executed
					log.info(title+" placing bracket orders @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					forexPositionTracker.placeBracketOrders();
					return;
				}else if(!forexPositionTracker.isClosed()){
					//nothing executed at all
					log.info(title+" close position @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					forexPositionTracker.close();
				}
			}else if(forexPositionTracker.hasLimitOrderReachedMaximumInactivePeriod()){
				IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
				if(limitOrderTracker!=null){				
					Order currentLimitOrder=limitOrderTracker.getOrder();
					if((currentLimitOrder.getOrderSide()==OrderSide.BUY && recommendation==Recommendation.SELL)||//If I am waiting to buy back and recommendation is sell, give it more time
							(currentLimitOrder.getOrderSide()==OrderSide.SELL && recommendation==Recommendation.BUY)){//If I am waiting to sell back and recommendation is buy, give it more time
						long currentLimitOrderMaximumInactivePeriod=forexPositionTracker.getLimitOrderMaximumInactivePeriod();
						long newLimitOrderMaximumInactivePeriod=2*currentLimitOrderMaximumInactivePeriod;
						forexPositionTracker.setLimitOrderMaximumInactivePeriod(newLimitOrderMaximumInactivePeriod);
						log.info(title+" lets give the limit order another 30 min @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
						if(isHistoryTrackingEnabled()){
							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
							addHistory(new LimitOrderPeriodExtendedEvent(currentTimeStamp, "extended limit order period", instrumentSpecification, latestForexPairStates));							
						}
						return;
					}
					log.info(title+" updating limit order because it reached max inactive period @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					updateLimitOrderForNoProfit(forexPositionTracker);					
					//limitOrderTracker.cancel(); update closer to current price but not get into a loss					
					if(forexPositionTracker.limitOrderUpdatedForNoProfit.getAndSet(true))//this will make sure it will only update it once
					{
						log.warn(title+" this shouldn't happen @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					}
					if(isHistoryTrackingEnabled()){
						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
						addHistory(new LimitOrderUpdatedForNoProfitEvent(currentTimeStamp, "updated limit order - inactive", instrumentSpecification, latestForexPairStates));							
					}
//					}
				}
			}else if(forexPositionTracker.getLimitOrderTracker()!=null&& !forexPositionTracker.limitOrderUpdatedForNoProfit.get()&&
					((forexPositionTracker.getLimitOrderTracker().getOrder().getOrderSide()==OrderSide.BUY && recommendation==Recommendation.BUY)//If I am waiting to buy back and recommendation is buy, update my limit order to buy back without profit right away
							||
							(forexPositionTracker.getLimitOrderTracker().getOrder().getOrderSide()!=OrderSide.BUY && recommendation==Recommendation.SELL)//If I am waiting to buy back and recommendation is buy, update my limit order to buy back without profit right away
					)
			){
				IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
				if(limitOrderTracker!=null){
					log.info(title+" updating limit order because signal changed to opposite while waitng @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					updateLimitOrderForNoProfit(forexPositionTracker);
					//limitOrderTracker.cancel(); update closer to current price but not get into a loss					
					if(forexPositionTracker.limitOrderUpdatedForNoProfit.getAndSet(true))//this will make sure it will only update it once
					{
						log.warn(title+" this shouldn't happen @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					}
					if(isHistoryTrackingEnabled()){
						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
						addHistory(new LimitOrderUpdatedForNoProfitEvent(currentTimeStamp, "updated limit order - opposite signal", instrumentSpecification, latestForexPairStates));							
					}
				}
			}else if(forexPositionTracker.limitOrderUpdatedForNoProfit.get() && closeOpenPositions){//perhaps wait 30 min? or 
				forexPositionTracker.close();
			}
			if(!forexPositionTracker.isClosed()){
				return;
			}
		}		
		if(recommendation==Recommendation.DO_NOTHING){
			return;
		}		
		double MAX_LONG_QUANTITY_PER_POSITION=400000;
		double MAX_SHORT_QUANTITY_PER_POSITION=-400000;
		//get current position
		if(ema.getBroker() instanceof IBPortfolioManagingBroker){
			IBPortfolioManagingBroker broker =(IBPortfolioManagingBroker)ema.getBroker();
			Portfolio portfolio=broker.getBrokerAccount().getPortfolio();
			IBPosition position=portfolio.hasPosition(instrumentSpecification) ? (IBPosition)portfolio.getPosition(instrumentSpecification) : null;
			if(position!=null){
				double currentQuantity=position.getQuantity();
				if(recommendation==Recommendation.BUY&&currentQuantity>=MAX_LONG_QUANTITY_PER_POSITION){
					return;
				}else if(recommendation==Recommendation.SELL&&currentQuantity<=MAX_SHORT_QUANTITY_PER_POSITION){
					return;
				}
			}
		}
		//if(hasRequestedToGoLong.get())return;//we already decided to open position once, don't buy once again
		//if(position!=null)return;//we already have a position, don't go deeper?
		Order order = new Order();		
		if(stopOpeningPositions){//don't submit new DAY orders(default) after 16:00, existing ones can work till 16:30 and then they get canceled by the exchange
			return;
		}
		order.setInstrumentSpecification(instrumentSpecification);
		order.setOrderTimeStamp(currentTimeStamp);
		if(recommendation==Recommendation.BUY){
			order.setOrderSide(OrderSide.BUY);
			order.setLimitPrice(askPrice);
		}else if(recommendation==Recommendation.SELL){
			order.setOrderSide(OrderSide.SELL);
			order.setLimitPrice(bidPrice);
		}
		//order.setOrderType(OrderType.LIMIT);
		order.setOrderType(OrderType.MARKET);
		double optimalQuantityUSD = CommissionsCalculator.determineOrderQuantityForOptimalMinimumCommission(order);
		double optimalQuantity = symbolCurrency==Currency.USD?optimalQuantityUSD:CurrencyPairs.convert(Currency.USD, optimalQuantityUSD, symbolCurrency);
		double roundedOptimalQuantity=CurrencyPairs.roundUpAmount(symbolCurrency, optimalQuantity);
		order.setQuantity(roundedOptimalQuantity);
		//double approximateCommission = CommissionsCalculator.determineApproximateCommissionForOrder(buyOrder);
		//log.info("approximateCommission="+approximateCommission+" quantity="+optimalQuantity);	
		//IPositionService positionService = instrumentAnalyzer.getPositionService();
		ForexBracketPositionService positionService = (ForexBracketPositionService)instrumentAnalyzer.getPositionService();
		instrumentAnalyzer.setPositionTracker(positionService.openPosition(order,0,thePairStateHistory));
	}

	private void updateLimitOrderForNoProfit(ForexBracketPositionService.Tracker forexPositionTracker){
		IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
		Order currentLimitOrder=limitOrderTracker.getOrder();
		double limitOrderAveragePrice=forexPositionTracker.getLimitOrderAveragePrice();
		double limitOrderFilledQuantity=forexPositionTracker.getLimitOrderFilledQuantity();
		double limitOrderCommission=forexPositionTracker.getLimitOrderCommission();
		Currency limitOrderCommissionCurrency=forexPositionTracker.getLimitOrderCommissionCurrency();
		double mainOrderAveragePrice=forexPositionTracker.getMainOrderAveragePrice();
		double mainOrderFilledQuantity=forexPositionTracker.getMainOrderFilledQuantity();
		double mainOrderCommission=forexPositionTracker.getMainOrderCommission();
		Currency mainOrderCommissionCurrency=forexPositionTracker.getMainOrderCommissionCurrency();
		double stopOrderAveragePrice=forexPositionTracker.getStopOrderAveragePrice();
		double stopOrderFilledQuantity=forexPositionTracker.getStopOrderFilledQuantity();
		double stopOrderCommission=forexPositionTracker.getStopOrderCommission();
		Currency stopOrderCommissionCurrency=forexPositionTracker.getStopOrderCommissionCurrency();
		InstrumentSpecification instrumentSpecification=instrumentAnalyzer.getInstrumentSpecification();
		String symbol = instrumentSpecification.getSymbol().toString();
		Currency quoteCurrency = instrumentSpecification.getCurrency();
		double mainOrderCommissionInQuoteCurrency=(quoteCurrency==mainOrderCommissionCurrency)? mainOrderCommission : CurrencyPairs.convert(mainOrderCommissionCurrency, mainOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		double stopOrderCommissionInQuoteCurrency=0;
		if(stopOrderCommissionCurrency!=null){
			stopOrderCommissionInQuoteCurrency=(quoteCurrency==stopOrderCommissionCurrency)? stopOrderCommission : CurrencyPairs.convert(stopOrderCommissionCurrency, stopOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		}
		double limitOrderCommissionInQuoteCurrency=0;
		if(limitOrderCommissionCurrency!=null){
			limitOrderCommissionInQuoteCurrency=(quoteCurrency==limitOrderCommissionCurrency)? limitOrderCommission : CurrencyPairs.convert(limitOrderCommissionCurrency, limitOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		}						
		double totalCommissionsInQuoteCurrency=mainOrderCommissionInQuoteCurrency+stopOrderCommissionInQuoteCurrency+limitOrderCommissionInQuoteCurrency;
		double outstandingBalance=(mainOrderAveragePrice*mainOrderFilledQuantity)+totalCommissionsInQuoteCurrency-(limitOrderAveragePrice*limitOrderFilledQuantity)-(stopOrderAveragePrice*stopOrderFilledQuantity);
		double outstandingQuantity=mainOrderFilledQuantity-stopOrderFilledQuantity-limitOrderFilledQuantity;
		double price=outstandingBalance/outstandingQuantity;
		Order updateLimitOrder=new Order(currentLimitOrder);
		updateLimitOrder.setId(null);
		if(updateLimitOrder.getOrderSide()==OrderSide.BUY){
			price=CurrencyPairs.roundCurrencyPrice(Currency.toClass(symbol), quoteCurrency, price, BigDecimal.ROUND_DOWN);
		}else{
			price=CurrencyPairs.roundCurrencyPrice(Currency.toClass(symbol),quoteCurrency, price, BigDecimal.ROUND_UP);
		}
		updateLimitOrder.setLimitPrice(price);
		updateLimitOrder.setQuantity(outstandingQuantity);
		if (forexPositionTracker.isSimulateOrderUpdatesEnabled()) {
			if (!forexPositionTracker.pendingChainedLimitOrderHolder.compareAndSet(null, updateLimitOrder)) {
				throw new IllegalStateException("another limit update is pending: can not proceed");
			}
			limitOrderTracker.cancel();
		} else {
			limitOrderTracker.update(updateLimitOrder);
		}

	}

	public static void closeForexPositionIfNotWorkingAnything(IBPosition pos, InstrumentAnalyzer instrumentAnalyzer){
		ForexBracketPositionService.Tracker forexPositionTracker=(ForexBracketPositionService.Tracker)instrumentAnalyzer.getPositionTracker();
		if(forexPositionTracker==null || forexPositionTracker.isClosed()){
			Order order = new Order();
			TimeStamp currentTimeStamp=instrumentAnalyzer.getEMA().getTimeStampService().currentTimeStamp();
			order.setInstrumentSpecification(instrumentAnalyzer.getInstrumentSpecification());
			order.setOrderTimeStamp(currentTimeStamp);
			order.setOrderSide(pos.getQuantity()>0?OrderSide.SELL:OrderSide.BUY);
			order.setOrderType(OrderType.MARKET);
			double quantity = Math.abs(pos.getQuantity());
			order.setQuantity(quantity);
			ForexBracketPositionService positionService = (ForexBracketPositionService)instrumentAnalyzer.getPositionService();
			instrumentAnalyzer.setPositionTracker(positionService.openPosition(order, quantity, new LinkedList<ForexPairStates>()));					
		}
	}        
	/**
	 * goes through the associated trends and adds its ChangeDetails values to the given currentPairChangeDetails(Set&lt;ChangeDetails&gt;)
	 * @param upPairChangeDetails
	 * @param downPairChangeDetails
	 * @param noChangePairDetails
	 */
	public void getChangeDetailsLists(Set<ChangeDetails> currentPairChangeDetails){
		for(Entry<String, ChangeDetails> trendEntry : trends.entrySet()){
			//String pair=trendEntry.getKey();
			ChangeDetails changeDetails=trendEntry.getValue();
//			if(changeDetails.getChange()==Change.NoChange){
//				noChangePairDetails.add(changeDetails);
//			}else if(changeDetails.getChange()==Change.Down){
//				downPairChangeDetails.add(changeDetails);
//			}else if(changeDetails.getChange()==Change.Up){
//				currentPairChangeDetails.add(changeDetails);
//			}
			currentPairChangeDetails.add(changeDetails);
		}
	}

	public boolean printCurrentAndMemoryStates(Set<ChangeDetails> currentPairChangeDetails, List<ForexPairStateRanges> uniqueSimilarCasesFromMemoryThisPair, double thisFastBidValue, double averageAlternativePercChange, double deviation, double percChange){
		StringBuffer msg=new StringBuffer();
//		msg.append("current:");
//		for(ChangeDetails cd:currentPairChangeDetails){
//			msg.append(cd.toString()+" ");
//		}
//		double fastBidValueFromMemory=0;
		double averageAlternativePercChangeFromMemory=0;
		double deviationFromMemory=0;
		double percChangeFromMemory=0;
//		instrumentAnalyzer.getLog().info(msg.toString());
//		msg=new StringBuffer();
		for(ForexPairStateRanges forexPairStateRanges: uniqueSimilarCasesFromMemoryThisPair){
			HashSet<StopLossClosedEvent> longStopLossClosedEventsThisPair = forexPairStateRanges.getLongLossOutcomes();
			HashSet<StopLossClosedEvent> shortStopLossClosedEventsThisPair = forexPairStateRanges.getShortLossOutcomes();
			HashSet<ProfitLimitClosedEvent> longProfitLimitClosedEventsThisPair = forexPairStateRanges.getLongProfitOutcomes();
			HashSet<ProfitLimitClosedEvent> shortProfitLimitClosedEventsThisPair = forexPairStateRanges.getShortProfitOutcomes();
			if(longStopLossClosedEventsThisPair!=null){
//				msg.append("memory longStopLoss:");
				for(StopLossClosedEvent event:longStopLossClosedEventsThisPair){
					ForexPairStates forexPairStates=event.getForexPairStates();
//					for(ChangeDetails cd:forexPairStates.getCurrentPairs()){
//						msg.append(cd.toString()+" ");
//						if(cd.getForexPair().equals(thisForexPair)){
//							fastBidValueFromMemory=cd.getFastBidValue();
//						}
//					}
					averageAlternativePercChangeFromMemory=forexPairStates.getAverageAlternativePercChange();
					deviationFromMemory=forexPairStates.getDeviation();
					percChangeFromMemory=forexPairStates.getPercChange();
				}
			}
			if(shortStopLossClosedEventsThisPair!=null){
//				msg.append("memory shortStopLoss:");
				for(StopLossClosedEvent event:shortStopLossClosedEventsThisPair){
					ForexPairStates forexPairStates=event.getForexPairStates();
//					for(ChangeDetails cd:forexPairStates.getCurrentPairs()){
//						msg.append(cd.toString()+" ");
//						if(cd.getForexPair().equals(thisForexPair)){
//							fastBidValueFromMemory=cd.getFastBidValue();
//						}
//					}
					averageAlternativePercChangeFromMemory=forexPairStates.getAverageAlternativePercChange();
					deviationFromMemory=forexPairStates.getDeviation();
					percChangeFromMemory=forexPairStates.getPercChange();
				}
			}
			if(longProfitLimitClosedEventsThisPair!=null){
//				msg.append("memory longProfitLimit:");
				for(ProfitLimitClosedEvent event:longProfitLimitClosedEventsThisPair){
					ForexPairStates forexPairStates=event.getForexPairStates();
//					for(ChangeDetails cd:forexPairStates.getCurrentPairs()){
//						msg.append(cd.toString()+" ");
//						if(cd.getForexPair().equals(thisForexPair)){
//							fastBidValueFromMemory=cd.getFastBidValue();
//						}
//					}
					averageAlternativePercChangeFromMemory=forexPairStates.getAverageAlternativePercChange();
					deviationFromMemory=forexPairStates.getDeviation();
					percChangeFromMemory=forexPairStates.getPercChange();
				}
			}
			if(shortProfitLimitClosedEventsThisPair!=null){
//				msg.append("memory shortProfitLimit:");
				for(ProfitLimitClosedEvent event:shortProfitLimitClosedEventsThisPair){
					ForexPairStates forexPairStates=event.getForexPairStates();
//					for(ChangeDetails cd:forexPairStates.getCurrentPairs()){
//						msg.append(cd.toString()+" ");
//						if(cd.getForexPair().equals(thisForexPair)){
//							fastBidValueFromMemory=cd.getFastBidValue();
//						}
//					}
					averageAlternativePercChangeFromMemory=forexPairStates.getAverageAlternativePercChange();
					deviationFromMemory=forexPairStates.getDeviation();
					percChangeFromMemory=forexPairStates.getPercChange();
				}
			}
		}

		//instrumentAnalyzer.getLog().info(msg.toString());
//		double deviationRatio=(deviation-deviationFromMemory)/deviationFromMemory;
//		instrumentAnalyzer.getLog().info(thisForexPair+" deviation="+deviation+" deviationFromMemory="+deviationFromMemory+" deviationRatio="+deviationRatio);


//		double fastBidValueRatio=(thisFastBidValue-fastBidValueFromMemory)/fastBidValueFromMemory;
//		instrumentAnalyzer.getLog().info(thisForexPair+" fastBidValue="+thisFastBidValue+" fastBidValueFromMem="+fastBidValueFromMemory+" fastBidValueRatio="+fastBidValueRatio);

//		double averageAlternativePercChangeRatio=(averageAlternativePercChange-averageAlternativePercChangeFromMemory)/averageAlternativePercChangeFromMemory;
//		instrumentAnalyzer.getLog().info(thisForexPair+" avgAltPercChange="+averageAlternativePercChange+" avgAltPercChangeMem="+averageAlternativePercChangeFromMemory+" averageAlternativePercChangeRatio="+averageAlternativePercChangeRatio);
//		return Math.abs(deviationRatio)<=0.01;
		double percChangeRatio=(percChange-percChangeFromMemory)/percChangeFromMemory;
		double percChangeDiffFromMemory=percChangeFromMemory-averageAlternativePercChangeFromMemory;
		double percChangeDiff=percChange-averageAlternativePercChange;
		double percChangeDiffRatio=(percChangeDiff-percChangeDiffFromMemory)/percChangeDiffFromMemory;
		instrumentAnalyzer.getLog().info(thisForexPair+" percChange="+percChange+" percChangeFromMemory="+percChangeFromMemory+" percChangeRatio="+percChangeRatio+" percChangeDiffRatio="+percChangeDiffRatio);
		boolean result=Math.abs(percChangeDiffRatio)>=0.1;
		return result;
	}
}